
package com.shop.cloud.mall.api.controller;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.shop.cloud.common.core.constant.CommonConstants;
import com.shop.cloud.common.core.constant.SecurityConstants;
import com.shop.cloud.common.core.util.R;
import com.shop.cloud.common.data.tenant.TenantContextHolder;
import com.shop.cloud.common.portal.annotation.ApiLogin;
import com.shop.cloud.common.portal.constant.PortalConstants;
import com.shop.cloud.common.portal.entity.ThirdSession;
import com.shop.cloud.common.portal.util.ThirdSessionHolder;
import com.shop.cloud.mall.api.service.BalanceRechargeConfigService;
import com.shop.cloud.mall.common.config.MallConfigProperties;
import com.shop.cloud.mall.common.constant.MallConstants;
import com.shop.cloud.mall.common.constant.MallReturnCode;
import com.shop.cloud.mall.common.entity.*;
import com.shop.cloud.mall.api.service.UserBalanceRecordService;
import com.shop.cloud.pay.common.dto.AliBaseRequest;
import com.shop.cloud.pay.common.entity.PayConfig;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * 用户余额记录
 *
 * @author 
 * @date 2023-11-01 10:21:44
 */
@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("/userbalancerecord")
@Tag(description = "userbalancerecord", name = "用户余额记录API")
public class UserBalanceRecordController {

    private final UserBalanceRecordService userBalanceRecordService;
    private final com.shop.cloud.pay.common.feign.FeignPayService feignPayService;
    private final MallConfigProperties mallConfigProperties;
    private final BalanceRechargeConfigService balanceRechargeConfigService;

    /**
     * 用户余额记录分页列表
     * @param page 分页对象
     * @param userBalanceRecord 用户余额记录
     * @return
     */
    @Operation(summary = "用户余额记录分页列表")
    @GetMapping("/page")
    @ApiLogin
    public R getPage(Page page, UserBalanceRecord userBalanceRecord) {
        userBalanceRecord.setIsPay(CommonConstants.YES);
        return R.ok(userBalanceRecordService.page(page, Wrappers.query(userBalanceRecord)));
    }

    /**
     * 通过id查询用户余额记录
     * @param id
     * @return R
     */
    @Operation(summary = "通过id查询用户余额记录")
    @GetMapping("/{id}")
    @ApiLogin
    public R getById(@PathVariable("id") String id) {
        return R.ok(userBalanceRecordService.getById(id));
    }

    /**
     * 新增用户余额记录
     * @param userBalanceRecord
     * @return R
     */
    @Operation(summary = "新增用户余额记录")
    @PostMapping
    @ApiLogin
    public R save(@RequestBody UserBalanceRecord userBalanceRecord){
        //余额充值
        if(MallConstants.USER_BALANCE_RECORD_TYPE_1.equals(userBalanceRecord.getType())){
            userBalanceRecord.setUserId(ThirdSessionHolder.getUserId());
            userBalanceRecord.setIsPay(CommonConstants.NO);
            userBalanceRecord.setRecordNo(IdUtil.getSnowflake(0,0).nextIdStr());
            if(StrUtil.isNotBlank(userBalanceRecord.getRelationId())){
                //有余额充值配置ID，获取赠送金额
                BalanceRechargeConfig balanceRechargeConfig = balanceRechargeConfigService.getById(userBalanceRecord.getRelationId());
                userBalanceRecord.setPayAmount(balanceRechargeConfig.getPriceAmount());
                userBalanceRecord.setGiveAmount(balanceRechargeConfig.getGiveAmount());
                userBalanceRecord.setAmount(balanceRechargeConfig.getPriceAmount().add(balanceRechargeConfig.getGiveAmount()));
            }else{
                //手机输入金额
                userBalanceRecord.setPayAmount(userBalanceRecord.getAmount());
                userBalanceRecord.setGiveAmount(BigDecimal.ZERO);
            }
            userBalanceRecordService.save(userBalanceRecord);
        }
        return R.ok(userBalanceRecord);
    }

    /**
     * 调用统一下单接口，并组装生成支付所需参数对象.
     *
     * @param userBalanceRecord 统一下单请求参数
     * @return 返回 {@link com.github.binarywang.wxpay.bean.order}包下的类对象
     */
    @Operation(summary = "调用统一下单接口")
    @PostMapping("/unifiedrecharge")
    @ApiLogin
    public R unifiedRecharge(HttpServletRequest request, @RequestBody UserBalanceRecord userBalanceRecord){
        String returnUrl = userBalanceRecord.getReturnUrl();
        String quitUrl = userBalanceRecord.getQuitUrl();
        String tradeType = userBalanceRecord.getTradeType();
        String paymentType = userBalanceRecord.getPaymentType();
        String appId = userBalanceRecord.getAppId();
        ThirdSession thirdSession = ThirdSessionHolder.getThirdSession();
        userBalanceRecord = userBalanceRecordService.getById(userBalanceRecord.getId());
        if(userBalanceRecord == null){
            return R.failed(MallReturnCode.ERR_70005.getCode(), MallReturnCode.ERR_70005.getMsg());
        }
        if(!CommonConstants.NO.equals(userBalanceRecord.getIsPay())){//只有未支付的详单能发起支付
            return R.failed(MallReturnCode.ERR_70004.getCode(), MallReturnCode.ERR_70004.getMsg());
        }
        String body = "余额充值:"+userBalanceRecord.getPayAmount();
        body = body.length() > 40 ? body.substring(0,39) : body;
        //获取支付配置
        R<PayConfig> r = feignPayService.getPayConfigByType(paymentType, SecurityConstants.FROM_IN);
        if(!r.isOk()){
            return R.failed(r.getMsg());
        }
        PayConfig payConfig = r.getData();
        if(payConfig == null){
            return R.failed("支付未配置");
        }
        //微信支付
        if(MallConstants.ORDER_PAYMENT_TYPE_1.equals(paymentType)){
            //更新编号防止不同终端微信报重复订单号
            //userBalanceRecord.setRecordNo(IdUtil.getSnowflake(0,0).nextIdStr());
            userBalanceRecordService.updateById(userBalanceRecord);
            WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest = new WxPayUnifiedOrderRequest();
            wxPayUnifiedOrderRequest.setSubMchId(payConfig.getSpecialMchId());
            wxPayUnifiedOrderRequest.setBody(body);
            wxPayUnifiedOrderRequest.setOutTradeNo(userBalanceRecord.getId());
            wxPayUnifiedOrderRequest.setTotalFee(userBalanceRecord.getPayAmount().multiply(new BigDecimal(100)).intValue());
            wxPayUnifiedOrderRequest.setTradeType(tradeType);
            wxPayUnifiedOrderRequest.setNotifyUrl(mallConfigProperties.getNotifyHost()+MallConstants.SERVER_ROUTE_NAME_MALLAPI+ PortalConstants.RECHARGE_NOTIFY_URL_WX+"/"+userBalanceRecord.getTenantId());
            wxPayUnifiedOrderRequest.setSpbillCreateIp(request.getRemoteAddr());
            wxPayUnifiedOrderRequest.setSubOpenid(thirdSession.getOpenId());
            wxPayUnifiedOrderRequest.setSubAppId(thirdSession.getAppId());
            wxPayUnifiedOrderRequest.setAppid(appId);
            return feignPayService.unifiedOrderWx(wxPayUnifiedOrderRequest, SecurityConstants.FROM_IN);
        }else if(MallConstants.ORDER_PAYMENT_TYPE_2.equals(paymentType)){//支付宝支付
            Map<String, Object> params = new HashMap<>();
            params.put("tradeType",tradeType);
            params.put("outTradeNo",userBalanceRecord.getId());
            params.put("totalAmount",userBalanceRecord.getPayAmount().toString());
            params.put("notifyUrl",mallConfigProperties.getNotifyHost()+MallConstants.SERVER_ROUTE_NAME_MALLAPI + PortalConstants.RECHARGE_NOTIFY_URL_ALI + "/" + userBalanceRecord.getTenantId());
            params.put("returnUrl",returnUrl);
            params.put("quitUrl",quitUrl);
            params.put("subject",body);
            AliBaseRequest aliBaseRequest = new AliBaseRequest();
            aliBaseRequest.setAppAuthToken(payConfig.getSpecialMchId());
            aliBaseRequest.setParams(params);
            return feignPayService.unifiedOrderAli(aliBaseRequest, SecurityConstants.FROM_IN);
        }else{
            return R.failed("无此支付类型");
        }
    }

    /**
     * 支付回调（微信）
     * @param xmlData
     * @return
     * @throws WxPayException
     */
    @Operation(summary = "支付回调（微信）")
    @PostMapping("/notify-order-wx/{tenantId}")
    public String notifyRechargeWx(@RequestBody String xmlData, @PathVariable("tenantId") String tenantId) {
        log.info("支付回调（微信）:"+xmlData);
        TenantContextHolder.setTenantId(tenantId);//设置租户ID
        R<WxPayOrderNotifyResult> r = feignPayService.notifyOrderWx(xmlData, SecurityConstants.FROM_IN);
        if(r.isOk()){
            WxPayOrderNotifyResult notifyResult = r.getData();
            UserBalanceRecord userBalanceRecord = userBalanceRecordService.getById(notifyResult.getOutTradeNo());
            if(userBalanceRecord != null){
                userBalanceRecord.setTradeType(notifyResult.getTradeType());
                if(userBalanceRecord.getPayAmount().multiply(new BigDecimal(100)).intValue() == notifyResult.getTotalFee()){
                    userBalanceRecord.setTransactionId(notifyResult.getTransactionId());
                    userBalanceRecord.setPaymentType(MallConstants.ORDER_PAYMENT_TYPE_1);
                    userBalanceRecordService.notifyUserBalanceRecharge(userBalanceRecord);
                    return WxPayNotifyResponse.success("成功");
                }else{
                    return WxPayNotifyResponse.fail("付款金额与充值记录金额不等");
                }
            }else{
                return WxPayNotifyResponse.fail("无此充值记录");
            }
        }else{
            return WxPayNotifyResponse.fail(r.getMsg());
        }
    }

    /**
     * 支付回调（支付宝）
     * @param request
     * @return
     * @throws
     */
    @Operation(summary = "支付回调（支付宝）")
    @ResponseBody
    @PostMapping("/notify-order-ali/{tenantId}")
    public String notifyOrderAli(HttpServletRequest request, @PathVariable("tenantId") String tenantId) {
        log.info("支付回调（支付宝）");
        Map<String, String> params = new HashMap<>();
        Map requestParams = request.getParameterMap();
        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
            }
            params.put(name, valueStr);
        }
        String appId = request.getParameter("app_id");
        if(StrUtil.isBlank(appId)){
            return "appId不能为空";
        }
        TenantContextHolder.setTenantId(tenantId);//设置租户ID
        UserBalanceRecord userBalanceRecord = userBalanceRecordService.getById(request.getParameter("out_trade_no"));
        if(userBalanceRecord == null){
            log.error("无此充值记录out_trade_no："+request.getParameter("out_trade_no"));
            return "无此充值记录";
        }
        if(userBalanceRecord.getPayAmount().compareTo(new BigDecimal(request.getParameter("total_amount"))) == 0){
            userBalanceRecord.setTransactionId(request.getParameter("trade_no"));
            userBalanceRecord.setPaymentType(MallConstants.ORDER_PAYMENT_TYPE_2);
            userBalanceRecordService.notifyUserBalanceRecharge(userBalanceRecord);
            log.info("支付回调（支付宝）success");
            return "success";
        }else{
            log.error("付款金额与充值记录金额不等out_trade_no："+request.getParameter("out_trade_no"));
            return "付款金额与充值记录金额不等";
        }

    }

    /**
     * 通过用户余额记录各类型总金额
     * @param type
     * @param userId
     * @return R
     */
    @Operation(summary = "通过用户余额记录各类型总金额")
    @GetMapping("/sum/{type}/{userid}")
    @ApiLogin
    public R getSum(@PathVariable("type") String type, @PathVariable("userid") String userId) {
        QueryWrapper<UserBalanceRecord> wrapper = new QueryWrapper<>();
        wrapper.select("sum(amount) as sum_amount");
        wrapper.eq("`type`", type);
        wrapper.eq("user_id", userId);
        wrapper.eq("is_pay", CommonConstants.YES);
        Map<String, Object> map = userBalanceRecordService.listMaps(wrapper).get(0);
        if(map != null){
            return R.ok(map.get("sum_amount"));
        }else{
            return R.ok(0);
        }
    }
}
